using System;
using System.Collections.Generic;
using System.Text;

using Weazel.Microblaze.Simulator.Engine;

namespace Weazel.Microblaze.Simulator.Components.Microblaze.Instructions
{
    /// <summary>
    /// Arithmetic Reverse Subtract
    /// </summary>
    public class Rsub : Instruction
    {
        public enum RsubInstructionKind
        {
            None,  // Error
            rsubkc, // add with carry, keep carry
            rsubc,  // add with carry
            rsubk,  // add keep carry
            rsub
        }

        private RsubInstructionKind kind;

        public override int CycleCount
        {
            get { return 1; }
        }

        public Rsub()
            : base("rsub")
        {
        }

        public override void Set(uint address, uint value)
        {
            base.Set(address, value);

            this.kind =
              (Opcode & 0x07) == 0x07 ? RsubInstructionKind.rsubkc :
              (Opcode & 0x07) == 0x03 ? RsubInstructionKind.rsubc :
              (Opcode & 0x07) == 0x05 ? RsubInstructionKind.rsubk :
              (Opcode & 0x07) == 0x01 ? RsubInstructionKind.rsub :
               RsubInstructionKind.None;

            if (kind == RsubInstructionKind.None)
                throw new UnknownInstructionException(value);
        }

        /// <summary>
        /// The contents of register rA is subtracted from the contents of register 
        /// rB and the result is placed into register rD. Bit 3 of the instruction
        /// (labeled as K in the figure) is set to a one for the mnemonic rsubk. Bit 
        /// 4 of the instruction (labeled as C in the figure) is set to a one for 
        /// the mnemonic rsubc. Both bits are set to a one for the mnemonic rsubkc. 
        /// When an rsub instruction has bit 3 set (rsubk, rsubkc), the carry flag 
        /// will Keep its previous value regardless of the outcome of the execution 
        /// of the instruction. If bit 3 is cleared (rsub, rsubc), then the carry 
        /// flag will be affected by the execution of the instruction. When bit 4 
        /// of the instruction is set to a one (rsubc, rsubkc), the content of the 
        /// carry flag (MSR[C]) affects the execution of the instruction. When bit 
        /// 4 is cleared (rsub, rsubk), the content of the carry flag does not 
        /// affect the execution of the instruction (providing a normal 
        /// subtraction).
        /// </summary>    
        public override void Execute(MicroblazeComponent MicroblazeComponent)
        {
            bool c =
              kind == RsubInstructionKind.rsubc ||
              kind == RsubInstructionKind.rsubkc;

            bool k =
              kind == RsubInstructionKind.rsubk ||
              kind == RsubInstructionKind.rsubkc;

            int ra = (int)MicroblazeComponent.ReadRegister(SourceRegisterA);
            int rb = (int)MicroblazeComponent.ReadRegister(SourceRegisterB);

            uint carry = MicroblazeComponent.ArithmeticCarry ? (uint)1 : (uint)0;

            long notra = ~((long)ra);

            long result = !c ?
              (long)(rb) + (long)(notra + 1) :
              (long)(rb) + (long)(notra + carry);

            MicroblazeComponent.WriteRegister(DestinationRegister, (uint)result, Address);

            if (!k)
            {
                MicroblazeComponent.ArithmeticCarry = result > 0xffffffff;
            }
        }

        public override string ToString()
        {
            return string.Format("{0} r{1}, r{2}, r{3}",
              kind,
              DestinationRegister,
              SourceRegisterA,
              SourceRegisterB
            );
        }
    }
}
